#include "conexion.h"
#include <stdio.h>
#include <iostream>
#include "excepciones/excepcion_cola.h"
#include "excepciones/excepcion_conexion.h"
#include "logger.h"
#include <sys/time.h>
#include <stdlib.h>
#include <sstream>

Conexion::Conexion(){
    conectado = false;
    thread = NULL;
}

static std::string generarNombreUnico(){
    srand(time(NULL));
    std::stringstream s;
    struct timeval tiempo;
    gettimeofday(&tiempo, NULL);
    s << random();
    s << tiempo.tv_usec;
    return s.str();
}

void Conexion::conectar(std::string direccion) throw(){
    //Abro la cola con el nombre dado y envío un mensaje con el nombre
    //de la cola en la cual espero una repuesta. La respuesta debe ser
    //el nombre de otra cola que utilizo para transmitir datos.
    try{
	Logger::log("Abro la cola " + direccion + "\n");

	Cola cola;
	cola.conectar(direccion);

	std::string nombreColaRecepcion(std::string("/colaRecepcion") + "cliente" + generarNombreUnico());
	Mensaje mensaje(nombreColaRecepcion.c_str(), nombreColaRecepcion.size());

	Logger::log("Nombre de la cola de recepcion " + nombreColaRecepcion + "\n");

	cola.enviar(mensaje);
	cola.cerrar();

	colaReceptora.conectar(nombreColaRecepcion);
	Mensaje recibido = colaReceptora.recibir();
	std::string nombreEmision(recibido.getBuffer());

	colaEmisora.conectar(nombreEmision);
	conectado = true;
    }catch(ExcepcionCola e){
	Logger::log("Error al iniciar la conexion.\n");
	throw ExcepcionConexion();
    }
}

bool Conexion::estaConectado(){
    return conectado;
}

void Conexion::registrarCallbackRecepcion(CallbackConexion *cc)throw(){
    if(!estaConectado())
	throw ExcepcionConexion();
    
    callback = cc;
    thread = new ThreadConexion(this);
    thread->start();
}

void Conexion::cerrar(){
    conectado=false;
    if(thread){
	Logger::log("Le pido al thread receptor que frene\n");
	thread->stop();
	Mensaje cerrar("hack", 4);
	colaReceptora.enviar(cerrar);
    }

    Logger::log("Cierro las colas y destruyo las que me corresponden\n");
    colaReceptora.cerrar();
    colaReceptora.destruir();
    colaEmisora.cerrar();
    //La emisora no la destuyo porque no me pertenece
    colaEscucha.cerrar();
    colaEscucha.destruir();
    
    if(thread){
	Logger::log("Elimino el thread\n");
	delete thread;
	thread = NULL;
    }
}

void Conexion::enviar(Mensaje &m)throw(){
    try{
	colaEmisora.enviar(m);
    }catch(ExcepcionCola e){
	throw ExcepcionConexion();
    }
}

Mensaje Conexion::recibir()throw(){
    try{
	return colaReceptora.recibir();
    }catch(ExcepcionCola e){
	throw ExcepcionConexion();
    }
}

void Conexion::escuchar(std::string nombre){
    colaEscucha.conectar(nombre);
}

Conexion* Conexion::aceptar()throw(){
    try{
	Mensaje recibido = colaEscucha.recibir();
	std::string nombreEmisor(recibido.getBuffer());
	
	Conexion *nuevaConexion = new Conexion();
	nuevaConexion->colaEmisora.conectar(nombreEmisor);
	
	std::string nombreColaRecepcion(std::string("/colaRecepcion") + "servidor" + generarNombreUnico());
	Mensaje mensaje(nombreColaRecepcion.c_str(), nombreColaRecepcion.size());
	
	nuevaConexion->colaReceptora.conectar(nombreColaRecepcion);
	nuevaConexion->colaEmisora.enviar(mensaje);
	return nuevaConexion;

    }catch(ExcepcionCola e){
	Logger::log("Error al aceptar una conexion.\n");
	throw ExcepcionConexion();
    }
}
